package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.constant.RedisConst;
import com.netflix.ribbon.proxy.annotation.Http;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @author 李旭
 * @date 2021/11/26 15:36
 * @Description: 关于登录的全局过滤器
 */
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {


    //路径匹配工具类
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public static final String TOKEN = "token";
    public static final String USERTEMPID = "userTempId";

    public static final String LOGINURL = "http://passport.gmall.com/login.html?originUrl=";
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${login.url}")
    private String[] loginUrl;

    //全局过滤器的执行方法

    /**
     * 网关底层技术 ：  Spring官方文档上写的 WebFlux
     * 不再是同学们学习的常用技术：Tomcat Servlet技术
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //String path = request.getPath().toString();
        String path = request.getURI().getPath();
        System.out.println("路径:" + path);
        String allPath = request.getURI().toString();
        System.out.println("完整的路径：" + allPath);

        //1:校验用户访问的资源是否为内部资源
        //  /api/product/inner/getSkuInfo/{skuId}   这就是内部资源
        //参数1：路径规则
        //参数2：路径
        if (antPathMatcher.match("/**/inner/**", path)) {
            //1.1:不是内部资源 放行
            //1.2:是内部资源  友情提示  无权访问  枚举
            return out(response,ResultCodeEnum.PERMISSION);
        }

        //2:校验用户是否登录
        String userId = getUserId(request);
        //2.1: 是否登录：异步   未登录   Result对象 （信息 Code 数据）
        if(antPathMatcher.match("/**/auth/**",path) && StringUtils.isEmpty(userId)){
            //需要登录的异步请求 同时 未登录
            return out(response,ResultCodeEnum.LOGIN_AUTH);
        }
        //2.2: 是否登录：同步   未登录  重定向到登录页面
        for (String url : loginUrl) { ///  url: trade.html  path: /trade.html
            if(path.contains(url) && StringUtils.isEmpty(userId)){
                //需要登录的同步请求  同时 未登录  重定向到登录页面去
                response.setStatusCode(HttpStatus.SEE_OTHER);//303
                try {
                    //重定向的路径
                    response.getHeaders().add(HttpHeaders.LOCATION,
                            LOGINURL + URLEncoder.encode(allPath,"utf-8"));
                    //响应
                    return response.setComplete();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
        }
        //首页 搜索  详情  加入购物车 去登录  注册   都不需要登录 直接访问  可登录也可以不登录

        //3:准备放行的时候   如果不为空或NULL 可以传递用户的ID了  请求头
        if(!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId);
        }
        //4：准备放行的时候  如果未登录 传递临时用户ID
        String userTempId = getUserTempId(request);
        if(!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId);
        }


        //放行
        return chain.filter(exchange);
    }

    //获取临时用户ID
    private String getUserTempId(ServerHttpRequest request) {
        //1:从请求头中获取
        String userTempId = request.getHeaders().getFirst(USERTEMPID);
        if (StringUtils.isEmpty(userTempId)) {
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if (null != httpCookie) {
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }

    //统一返回结果
    private Mono<Void> out(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> result = Result.build(null, resultCodeEnum);
        String json = JSONObject.toJSONString(result);
        DataBufferFactory dataBufferFactory = response.bufferFactory();
        DataBuffer dataBuffer = dataBufferFactory.wrap(json.getBytes());
        //解决乱码问题  Content-Type: text/html; charset=utf-8  刷新页面
        //解决乱码问题  Content-Type: application/json; charset=utf-8  Ajax请求
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    //从缓存中获取用户ID
    private String getUserId(ServerHttpRequest request) {
        //1:获取令牌 token   不知道是异步还是同步
        String token = request.getHeaders().getFirst(TOKEN);
        if (StringUtils.isEmpty(token)) {
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if (null != httpCookie) {
                token = httpCookie.getValue();
            }
        }
        if (!StringUtils.isEmpty(token)) {
            //1.2:获取到令牌
            //2:通过令牌获取缓存中的UserId
            if (redisTemplate.hasKey(RedisConst.USER_LOGIN_KEY_PREFIX + token)) {
                //2.1:有 直接返回
                return (String) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + token);
            }
        }
        //2.2:没有 返回值null
        //1.1:未获取到令牌 直接返回值null
        return null;
    }


    //此全局过滤器的排序

    /**
     * LoginGlobalFilter : 我们自定义的全局过滤器
     * <p>
     * GateWay网关 ： 默认自带 9大过滤器
     * 完成网关的基础功能
     * 断言
     * 路由
     * ....
     * 共用 10大过滤器 == 9个自带 + 1个自己写的
     * <p>
     * 大约 3~4个是优先执行   自定义的建议在大约中间执行      大约是5后执行
     * 负整数最大      --- -1   --- 0     ---      1 --------- 正整数最大
     * 优化级最大  ---------------------------------------  优化级最小
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
